package p.sglmsn.top.antiduplication.handle;

import cn.hutool.log.Log;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import p.sglmsn.top.antiduplication.annotation.AntiDuplication;
import p.sglmsn.top.antiduplication.infe.UniqueCertificate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.DispatcherType;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class DuplicateRequestFiltering implements HandlerInterceptor {
    static Log log = Log.get();

    /**
     * 前置
     */
    static final String FRONT = "prevent_duplication:";
    protected static DuplicateRequestFiltering duplicateRequestFiltering;
    /**
     * 类型并发散列映射
     */
    ConcurrentHashMap<String, AntiDuplication.AntiDuplicationType> typeConcurrentHashMap = new ConcurrentHashMap<>(512);
    @Resource
    RedisTemplate<String, java.io.Serializable> redisTemplate;

    public static DuplicateRequestFiltering getDuplicateRequestFiltering() {
        return duplicateRequestFiltering;
    }

    public UniqueCertificate getUniqueCertificate() {
        return uniqueCertificate;
    }

    UniqueCertificate uniqueCertificate;

    private static String getK(HttpServletRequest request) {
        return FRONT + duplicateRequestFiltering.uniqueCertificate.pz(request) + request.getRequestURI();
    }

    /**
     * 清理当前防重复提交状态
     *
     * @return boolean
     */
    public static boolean clear() {
        try {
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = servletRequestAttributes.getRequest();
            duplicateRequestFiltering.redisTemplate.delete(getK(request));
        } catch (Exception e) {
            log.error("防重复提交清理失败", e);
        }
        return true;
    }

    public DuplicateRequestFiltering setRedisTemplate(RedisTemplate<String, java.io.Serializable> redisTemplate) {
        this.redisTemplate = redisTemplate;
        return this;
    }

    public DuplicateRequestFiltering setUniqueCertificate(UniqueCertificate uniqueCertificate) {
        this.uniqueCertificate = uniqueCertificate;
        return this;
    }

    @PostConstruct
    public void init() {
        DuplicateRequestFiltering.duplicateRequestFiltering = this;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        AntiDuplication.AntiDuplicationType antiDuplicationType = typeConcurrentHashMap.get(request.getRequestURI());
        if (AntiDuplication.AntiDuplicationType.无效过滤.equals(antiDuplicationType)) {
            return true;
        }
        String token = uniqueCertificate.pz(request);
        if (handler instanceof HandlerMethod && DispatcherType.REQUEST.equals(request.getDispatcherType())) {
            AntiDuplication methodAnnotation = ((HandlerMethod) handler).getMethodAnnotation(AntiDuplication.class);
            if (methodAnnotation != null) {
                AntiDuplication.AntiDuplicationType type = methodAnnotation.type();
                if (antiDuplicationType == null) {
                    typeConcurrentHashMap.put(request.getRequestURI(), type);
                }
                String k = getK(request);
                Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(k, token, methodAnnotation.time(), TimeUnit.MILLISECONDS);
                if (aBoolean != null && !aBoolean) {
//                    response.sendError(Integer.parseInt(ResultCode.duplicate_request_filtering.getCode()), methodAnnotation.msg());
                    response.setContentType("application/json;charset=UTF-8");
                    ServletOutputStream outputStream = response.getOutputStream();
                    ResponseEntity responseEntity = ResponseEntity.ok(methodAnnotation.msg());
                    outputStream.write(JSON.toJSONString(responseEntity).getBytes(StandardCharsets.UTF_8));
                    outputStream.flush();
                    outputStream.close();
                    if (log.isDebugEnabled()) {
                        log.debug("请求被拒绝{token:type:{},url:{}}", request.getDispatcherType(), request.getRequestURI());
                    }
                    return false;
                }
                return true;
            } else {
                typeConcurrentHashMap.put(request.getRequestURI(), AntiDuplication.AntiDuplicationType.无效过滤);
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AntiDuplication.AntiDuplicationType antiDuplicationType = typeConcurrentHashMap.get(request.getRequestURI());
        if (AntiDuplication.AntiDuplicationType.url过滤.equals(antiDuplicationType)) {
            redisTemplate.delete(getK(request));
        }

    }


}
